feat(graphrag): tenant_id column + scoped reads + backfill (RAN-38)#30
Merged
Conversation
Adds tenant_id to the three persisted GraphRAG tables (investigations, graph_snapshots, drain_templates), with a one-shot idempotent backfill so existing rows fall back to DefaultTenantID and a driver-specific composite PK promotion for drain_templates on SQLite/PostgreSQL. - schema.go: TenantID first on DrainTemplateRow; PK becomes (tenant_id, id). - investigation.go: TenantID + idx_investigations_tenant_created composite index; cooldown key now scoped by tenant; GetInvestigations / Get-by-id take a context and filter by storage.TenantFromContext. - snapshot.go: TenantID + idx_graph_snapshots_tenant_created; GetGraphSnapshot is ctx-scoped. takeSnapshot continues to fan out per tenant. - drain.go: SaveDrainTemplates / LoadDrainTemplates now take a tenant string; OnConflict targets composite (tenant_id, id) so the same template hash can coexist across tenants for the eventual per-tenant Drain miner. - migrate.go (new): AutoMigrateGraphRAG runs AutoMigrate, idempotent UPDATE backfill, and a SQLite (rename/recreate/copy) + PostgreSQL (DROP/ADD CONSTRAINT) PK promotion path. MySQL/MSSQL skipped with a log. - mcp/tools.go: thread the dispatcher ctx into get_investigations, get_investigation, get_graph_snapshot so the new ctx-aware signatures compile end-to-end. Subtask C (RAN-39) wires the request tenant. - builder.go / refresh.go: Drain Save/Load callers pin storage.DefaultTenantID for the single shared miner. Tests: - TestAutoMigrateGraphRAG_CreatesTenantCompositeIndexes - TestAutoMigrateGraphRAG_DrainTemplatesCompositePK - TestAutoMigrateGraphRAG_IsIdempotent - TestAutoMigrateGraphRAG_BackfillsLegacyRows - TestSaveLoadDrainTemplates_TenantIsolation (collision across tenants) - TestGraphRAG_GetInvestigations_TenantScoped (cross-tenant id-guess miss) - TestGraphRAG_GetGraphSnapshot_TenantScoped - TestCooldownKey_TenantIsolated Verification: go vet ./... and go test ./... — 281 tests pass across 26 packages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Paperclip <noreply@paperclip.ing>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Re-opens #28 (auto-closed when its base
ran37-graphrag-tenant-partitioned-storeswas deleted on RAN-37 merge). Same single commit, rebased ontomainpost-RAN-37.Summary
Subtask B of RAN-19. Adds
tenant_idto the three persisted GraphRAG tables —investigations,graph_snapshots,drain_templates— with composite indexes, scoped reads via ctx propagation, and a one-shot idempotent startup backfill so existing rows migrate tostorage.DefaultTenantID.investigationsandgraph_snapshotsget(tenant_id, created_at)composite indexes.drain_templatesPK is promoted to composite(tenant_id, id)so the same Drain template hash can coexist across tenants once a per-tenant miner ships.GetInvestigations,GetInvestigation, andGetGraphSnapshotnow take acontext.Contextand filter bystorage.TenantFromContext. Cross-tenant id-guess lookups returnErrRecordNotFound.PersistInvestigationwrites its tenant onto the row;takeSnapshotForTenantdoes the same;SaveDrainTemplates/LoadDrainTemplatesnow take atenantparameter and target the composite PK inOnConflict.investigationCooldownkeys now include tenant.AutoMigrateGraphRAGruns three passes — AutoMigrate → idempotentUPDATE … WHERE tenant_id IS NULL OR ''backfill across all three tables → driver-specific drain_templates PK promotion. SQLite uses the rename/recreate/copy/drop recipe; PostgreSQL usesALTER TABLE … DROP CONSTRAINT IF EXISTS … ADD PRIMARY KEY (tenant_id, id). MySQL/MSSQL skip with a logged warn.Out of scope
g.drainfield stays a single shared instance keyed underDefaultTenantIDfor now.Test plan
go vet ./...— cleango test ./internal/graphrag/... ./internal/mcp/... ./internal/storage/... -count=1— 200 tests passTestAutoMigrateGraphRAG_CreatesTenantCompositeIndexes— composite indexes materialised on SQLiteTestAutoMigrateGraphRAG_DrainTemplatesCompositePK— PRAGMA confirms (tenant_id, id) PKTestAutoMigrateGraphRAG_IsIdempotent— 3× re-run safeTestAutoMigrateGraphRAG_BackfillsLegacyRows— pre-seeded empty-tenant rows are filled with DefaultTenantIDTestSaveLoadDrainTemplates_TenantIsolation— colliding template IDs across tenants coexist; cross-tenant Load returns nothingTestGraphRAG_GetInvestigations_TenantScoped— id-guessing across tenants missesTestGraphRAG_GetGraphSnapshot_TenantScoped— same-instant snapshots stay isolatedTestCooldownKey_TenantIsolated🤖 Generated with Claude Code